#ifndef TLOG_H
#define TLOG_H

#include <QString>
#include <memory>
#include <string>
#include <unordered_map>
#include "spdlog/fmt/fmt.h"

#include "spdlog/spdlog.h"

#ifndef TSUFFIX
#define TSUFFIX(msg) std::string("").append("[")\
    .append(__FILE__).append(":").append(std::to_string(__LINE__)\
    .append(" ").append(__FUNCTION__))\
    .append("] ").append(msg).c_str()
#endif

#define TLOG_MAIN "main_log"

#ifdef WIN32
#define TTrace(msg,...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->trace(TSUFFIX(msg),__VA_ARGS__)
#define TDebug(...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->debug(__VA_ARGS__)
#define TInfo(...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->info(__VA_ARGS__)
#define TWarn(...) TLog::getLog(TLOG_MAIN)->getSpdLog()->warn(__VA_ARGS__)
#define TError(msg,...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->error(TSUFFIX(msg),__VA_ARGS__)
#define TCritical(...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->critical(__VA_ARGS__)

#define TLogTrace(id,msg,...)  TLog::getLog(id)->getSpdLog()->trace(TSUFFIX(msg),__VA_ARGS__)
#define TLogDebug(id,...)  TLog::getLog(id)->getSpdLog()->debug(__VA_ARGS__)
#define TLogInfo(id,...)  TLog::getLog(id)->getSpdLog()->info(__VA_ARGS__)
#define TLogWarn(id,...) TLog::getLog(id)->getSpdLog()->warn(__VA_ARGS__)
#define TLogError(id,msg,...)  TLog::getLog(id)->getSpdLog()->error(TSUFFIX(msg),__VA_ARGS__)
#define TLogCritical(id,...)  TLog::getLog(id)->getSpdLog()->critical(__VA_ARGS__)
#else
// GCC系必须要求有第一个参数开头，win则是整体将参数替换过来，因调用方一定会最少一个参数，故两者实际效果一样
#define TTrace(fmt,args...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->trace(TSUFFIX(fmt),##args)
#define TDebug(fmt,args...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->debug(fmt,##args)
#define TInfo(fmt,args...) TLog::getLog(TLOG_MAIN)->getSpdLog()->info(fmt,##args)
#define TWarn(fmt,args...) TLog::getLog(TLOG_MAIN)->getSpdLog()->warn(fmt,##args)
#define TError(fmt,args...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->error(TSUFFIX(fmt),##args)
#define TCritical(fmt,args...)  TLog::getLog(TLOG_MAIN)->getSpdLog()->critical(fmt,##args)

#define TLogTrace(id,msg,args...)  TLog::getLog(id)->getSpdLog()->trace(TSUFFIX(msg),##args)
#define TLogDebug(id,fmt,args...)  TLog::getLog(id)->getSpdLog()->debug(fmt,##args)
#define TLogInfo(id,fmt,args...)  TLog::getLog(id)->getSpdLog()->info(fmt,##args)
#define TLogWarn(id,fmt,args...) TLog::getLog(id)->getSpdLog()->warn(fmt,##args)
#define TLogError(id,msg,args...)  TLog::getLog(id)->getSpdLog()->error(TSUFFIX(msg),##args)
#define TLogCritical(id,fmt,args...)  TLog::getLog(id)->getSpdLog()->critical(fmt,##args)
#endif

// show file line and function
//#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE

class TLog
{
public:
    // register log and get log
    static void addLog(const std::string &id,const std::string &pathAndFile,int log_level= spdlog::level::trace);
    static TLog* getLog(const std::string &id = TLOG_MAIN);
    static void flushLog(const std::string &id = TLOG_MAIN);

    static void freeLog();
private:
    static std::unordered_map<std::string,TLog*> s_logs;

public:
    void flush();
    void setLevel(int level = spdlog::level::trace);

    std::shared_ptr<spdlog::logger> getSpdLog() {
        return m_logger;
    }

private:
    TLog(const std::string &id,const std::string &pathAndFile, int log_level);
    virtual ~TLog();

    void endLog();

    TLog(const TLog &other) = delete;
    TLog& operator=(const TLog &other) = delete;
    std::shared_ptr<spdlog::logger> m_logger;
};

// for more type visitor:https://fmt.dev/latest/api.html#formatting-user-defined-types
template <>
struct fmt::formatter<QString> {
    format_parse_context::iterator parse(format_parse_context& ctx) {
        auto it = ctx.begin(), end = ctx.end();
        if (it != end && *it != '}')
            throw format_error("invalid format");

        return it;
    }

    template <typename FormatContext>
    format_context::iterator format(const QString& p, FormatContext& ctx) {
        return format_to(
                    ctx.out(),
                    "{}",
                    p.toStdString());
    }
};


#endif // TLOG_H
